{
  File   : S2DMain.pas (This unit is a part of S2DE)
  S2DE   : Simple 2D Engine (software render)
  Copyright (c) 2008, Kagamma
  License: GPLv3

-----------------------------------------------------------
  Windows API, cac lenh xu ly input tu mouse & keyboard.
}

unit S2DMain;

{$MODE OBJFPC}

interface

uses
  Windows, SysUtils, S2DTypes, S2DSWCanvas, S2DLog;

var Screen: TBuffer;

// S2DE.DLL procedures and function
procedure SetGraphWindow(hWindow: hWnd; X, Y: LongInt); stdcall;
  external 's2de' name 'SetGraphWindow';
procedure CloseGraphWindow; stdcall;
  external 's2de' name 'CloseGraphWindow';
function  BlitBuffer(Buf: Pointer): Boolean; stdcall;
  external 's2de' name 'BlitBuffer';

// ve buffer Screen ra man hinh&  xu ly message
procedure GetMessages; overload;
// ve 1 buffer ra man hinh & xu ly message
procedure GetMessages(Buf: TBuffer); overload;
// khoi tao window voi kich thuoc sX, sY
procedure InitWindow(sX, sY: LongInt);
// thoat window
procedure CloseWindow;

implementation

procedure GetMessages; overload;
var Done: Boolean;
begin
  BlitBuffer(Pointer(Screen.Address));
  MouseMove  := False;
  Done       := FALSE;
  KeyMap[256]:= FALSE;
  while PeekMessage(S2DInfo.AMessage, 0, 0, 0, PM_REMOVE) AND NOT Done do
  begin
    if S2DInfo.AMessage.Message = WM_QUIT then
      S2DInfo.Running:= FALSE
    else
    begin
      TranslateMessage(S2DInfo.AMessage);
      DispatchMessage (S2DInfo.AMessage);
    end;
  end;
  Inc(S2DInfo.FPSCount);                      // Increment FPS Counter
  S2DInfo.FPSLast:= S2DInfo.FPSElapse;
  S2DInfo.FPSElapse := GetTickCount - S2DInfo.FPSStart;
  S2DInfo.FPSElapse := (S2DInfo.FPSLast + S2DInfo.FPSElapse) div 2;
  FrameTime:= S2DInfo.FPSElapse - S2DInfo.FPSLast;
end;

procedure GetMessages(Buf: TBuffer); overload;
var Done: Boolean;
begin
  BlitBuffer(Pointer(Buf.Address));
  MouseMove  := False;
  Done       := FALSE;
  KeyMap[256]:= FALSE;
  while PeekMessage(S2DInfo.AMessage, 0, 0, 0, PM_REMOVE) AND NOT Done do
  begin
    if S2DInfo.AMessage.Message = WM_QUIT then
      S2DInfo.Running:= FALSE
    else
    begin
      TranslateMessage(S2DInfo.AMessage);
      DispatchMessage (S2DInfo.AMessage);
    end;
  end;
  Inc(S2DInfo.FPSCount);                      // Increment FPS Counter
  S2DInfo.FPSLast:= S2DInfo.FPSElapse;
  S2DInfo.FPSElapse := GetTickCount - S2DInfo.FPSStart;
  S2DInfo.FPSElapse := (S2DInfo.FPSLast + S2DInfo.FPSElapse) div 2;
  FrameTime:= S2DInfo.FPSElapse - S2DInfo.FPSLast;
end;

function MainWinProc(AWindow: HWnd; AMessage: uInt; WParam: WParam;
                     LParam: LParam): LRESULT; stdcall; export;
begin
  Result:= 0;
  case AMessage of
    WM_ACTIVATE: ;

    WM_CREATE:
      S2DInfo.Running:= TRUE;
    WM_DESTROY:
      begin
        S2DInfo.Running:= FALSE;
        PostQuitMessage(0);
      end;
    WM_KEYUP:
      KeyMap[WParam]:= FALSE;
    WM_KEYDOWN:
      begin
        KeyMap[WParam]:= TRUE;
        KeyMap[256]:= TRUE;
      end;
    WM_MOUSEMOVE:
      begin
        MouseMove:= TRUE;
        MouseX:= LParam AND $FFFF;
        MouseY:= LParam shr 16;
        if WParam = MK_LBUTTON then
          MouseL:= TRUE;
        if WParam = MK_RBUTTON then
          MouseR:= TRUE;
        if WParam = MK_MBUTTON then
          MouseM:= TRUE;
      end;

    WM_LBUTTONDOWN:
      MouseL:= TRUE;
    WM_RBUTTONDOWN:
      MouseR:= TRUE;
    WM_MBUTTONDOWN:
      MouseM:= TRUE;

    WM_LBUTTONUP:
      MouseL:= FALSE;
    WM_RBUTTONUP:
      MouseR:= FALSE;
    WM_MBUTTONUP:
      MouseM:= FALSE;

    WM_TIMER :
      begin
        if wParam = 1 then
        begin
          S2DInfo.FPSCount := Round(S2DInfo.FPSCount * 1000/500);
// calculate to get per Second incase intercal is less or greater than 1 second
          S2DInfo.FPS:= S2DInfo.FPSCount;
          S2DInfo.FPSCount := 0;
          Result := 0;
        end;
      end;
  end;
  Result:= DefWindowProc(AWindow, AMessage, WParam, LParam);
end;

procedure ResetMouseState;
begin
  MouseL:= FALSE;
  MouseR:= FALSE;
  MouseM:= FALSE;
end;

procedure ResetKeyMap;
var I: Word;
begin
  for I:= 0 to 256 do
    KeyMap[I]:= FALSE;
end;

function  WindowRegister: Boolean;
var AClass: WndClass;
begin
  AClass.Style        := cs_ByteAlignWindow;
  AClass.lpfnWndProc  := WndProc(@MainWinProc);
  AClass.cbClsExtra   := 0;
  AClass.cbWndExtra   := 0;
  AClass.hInstance    := system.MainInstance;
  AClass.hIcon        := LoadIcon(0, idi_Application);
  AClass.hCursor      := LoadCursor(0, idc_Arrow);
  AClass.hbrBackground:= GetStockObject(BLACK_BRUSH);
  AClass.lpszMenuName := nil;
  AClass.lpszClassName:= 'S2DE';

  Result:= RegisterClass(AClass)<>0;
end;

function WinCreate: HWnd;
var hWindow: HWnd;
begin
  hwindow:=createwindowex(0,'S2DE', 'S2DE' ,WS_VISIBLE or WS_POPUP,0,0,50,50,0,0,system.MainInstance,nil);
  if hWindow<>0 then begin
    ShowWindow(hWindow, CmdShow);
    UpdateWindow(hWindow);
    S2DInfo.DC:= GetDC(hWindow);
  end;
  WinCreate:=hWindow;
  SetTimer(hWindow, 1, 500, nil);
end;

procedure InitWindow(sX, sY: LongInt);
begin
  {$IFDEF LOG}
  InitLog;
  {$ENDIF}
  WindowRegister;
  S2DInfo.WndHandle:= WinCreate;
  SetGraphWindow(S2DInfo.WndHandle, sX, sY);
  S2DInfo.Running:= TRUE;
  InitBuffer(Screen, sX, sY);
end;

procedure CloseWindow;
begin
  CloseGraphWindow;
  FreeBuffer(Screen);
  S2DInfo.Running:= FALSE;
  {$IFDEF LOG}
  CloseLog;
  {$ENDIF}
end;

begin
  ResetMouseState;
  ResetKeyMap;
end.

